//
// Created by DonAzufre on 2024/3/17.
//
#pragma once

#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include "IR.h"


class LLVMgen
{
private:
    std::stringstream llvmir;
    const std::vector<std::string> libFuncNames = {
        "getint",          "getfloat",      "getch",     "getarray", "getfarray", "putint",   "putfloat",
        "putch",           "putarray",      "putfarray", "putf",     "starttime", "stoptime", "llvm.memset.p0.i32",
        "_sysy_starttime", "_sysy_stoptime"};

    const std::string libFuncDeclaration =
        R"(declare i32 @getint()
declare i32 @getch()
declare float @getfloat()
declare i32 @getarray(i32*)
declare i32 @getfarray(float*)
declare void @putint(i32)
declare void @putch(i32)
declare void @putfloat(float)
declare void @putarray(i32 ,i32*)
declare void @putfarray(i32 ,float*)
declare void @llvm.memset.p0.i32(ptr, i8 ,i32, i1)
declare void @_sysy_starttime(i32)
declare void @_sysy_stoptime(i32)
)";

public:
    LLVMgen() : llvmir() {}

    std::string transform(sysy::Module *module)
    {
        llvmir << libFuncDeclaration;
        for (auto &name : module->getGlobalNames())

        {
            auto value = module->getGlobalValue(name);
            llvmir << '@' << *value << '\n';
        }

        llvmir << "\n\n";

        // for (auto& [name, func] : module->getFunctions())
        for (auto name : module->getFuncNames())
        {
            auto &func = module->getFunctions().at(name);
            if (std::find(libFuncNames.begin(), libFuncNames.end(), name) != libFuncNames.end())
            {
                // 是库函数
                // llvmir << "declare " << *func << "\n";
                continue;
            }

            llvmir << "define dso_local " << *func << "{\n";

            for (auto &bb : func->getBasicBlocks())
            {
                llvmir << bb->getName() << ":\n";

                for (auto &inst : bb->getInstructions())
                {
                    llvmir << "  " << inst->toString() << "\n";
                }

                llvmir << "\n";
            }

            llvmir << "}\n\n";
        }

        return llvmir.str();
    }
};
